﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace QQ2564874169.Nubbo
{
    public class ServiceManager
    {
        public event EventHandler<ServiceEventArgs> ServiceBefore;
        public event EventHandler<ServiceEventArgs> ServiceAfter;
        public event Func<Type, bool> ServiceFilter;
        public event EventHandler<RequestBeforeEventArgs> RequestBefore;
        public event EventHandler<RequestAfterEventArgs> RequestAfter; 

        private Dictionary<string, ServiceSetting> _settings = new Dictionary<string, ServiceSetting>(StringComparer.OrdinalIgnoreCase);
        private INubboServer _server;
        private NubboConfig _config;

        public ServiceManager(NubboConfig config)
        {
            _config = config;
        }

        public void LoadService(params Type[] types)
        {
            var target = typeof(IService);
            types = types.Where(i => i.IsClass && !i.IsAbstract && !i.IsGenericType).ToArray();

            foreach (var type in types)
            {
                if (type.GetInterfaces().All(i => i != target))
                    continue;
                var name = type.GetCustomAttribute<ServiceNameAttribute>() ??
                           new ServiceNameAttribute(type.FullName);
                if (_settings.ContainsKey(name.Name))
                    throw new ArgumentException($"已存在名为{name.Name}的服务。");

                if (ServiceFilter == null || ServiceFilter(type))
                {
                    _settings.Add(name.Name, new ServiceSetting
                    {
                        ServiceName = name,
                        ServiceType = type
                    });
                }
            }
        }

        public void Start(INubboServer server)
        {
            _server = server;
            _server.NewRequest += Server_NewRequest;
            _server.Start();
        }

        public NubboResponse GetResponse(NubboRequest request)
        {
            using (var resolver = _config.GetResolver())
            {
                RequestBefore?.Invoke(this, new RequestBeforeEventArgs
                {
                    Request = request,
                    Resolver = resolver
                });
                var respe = new RequestAfterEventArgs
                {
                    Response = new NubboResponse(resolver, request),
                    Request = request,
                    Resolver = resolver
                };
                RequestAfter?.Invoke(this, respe);
                return respe.Response;
            }
        }

        private void Server_NewRequest(object sender, ServiceRequest request)
        {
            if (_settings.ContainsKey(request.ServiceName) == false)
            {
                request.OnResponse(new ServiceResult
                {
                    Error = $"不存在名为{request.ServiceName}的服务。"
                });
                return;
            }
            var setting = _settings[request.ServiceName];

            using (var resolver = _config.GetResolver())
            {
                var service = resolver.Resolve(setting.ServiceType) as IService;
                var e = new ServiceEventArgs
                {
                    Context = new ServiceContext(setting.ServiceName)
                    {
                        Request = request
                    },
                    Resolver = resolver,
                    Service = service
                };
                ServiceBefore?.Invoke(this, e);

                if (e.Context.Result?.Data == null)
                {
                    try
                    {
                        service.Do(e.Context);
                    }
                    catch (Exception ex)
                    {
                        e.Context.Result.Error = ex.Message;
                    }
                }

                ServiceAfter?.Invoke(this, e);

                request.OnResponse(e.Context.Result);
            }
        }

        private class ServiceSetting
        {
            public Type ServiceType { get; set; }
            public ServiceNameAttribute ServiceName { get; set; }
        }
    }

    public class ServiceEventArgs : EventArgs
    {
        public IResolver Resolver { get; internal set; }
        public object Service { get; internal set; }
        public ServiceContext Context { get; internal set; }
    }

    public class RequestBeforeEventArgs : EventArgs
    {
        public IResolver Resolver { get; internal set; }
        public NubboRequest Request { get; internal set; }
    }

    public class RequestAfterEventArgs : EventArgs
    {
        public IResolver Resolver { get; internal set; }
        public NubboRequest Request { get; internal set; }
        public NubboResponse Response { get; set; }
    }
}
